package com.bihua.iot.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bihua.common.core.domain.PageQuery;
import com.bihua.common.core.page.TableDataInfo;
import com.bihua.common.exception.ServiceException;
import com.bihua.common.utils.BeanCopyUtils;
import com.bihua.common.utils.StringUtils;
import com.bihua.common.utils.redis.RedisUtils;
import com.bihua.iot.constant.Constants;
import com.bihua.iot.domain.Product;
import com.bihua.iot.domain.TdFields;
import com.bihua.iot.domain.TdSuperTable;
import com.bihua.iot.domain.bo.ProductBo;
import com.bihua.iot.domain.bo.ProductPropertiesBo;
import com.bihua.iot.domain.bo.ProductServicesBo;
import com.bihua.iot.domain.vo.ManufacturerVo;
import com.bihua.iot.domain.vo.ProductPropertiesVo;
import com.bihua.iot.domain.vo.ProductServicesVo;
import com.bihua.iot.domain.vo.ProductVo;
import com.bihua.iot.enums.DataTypeEnum;
import com.bihua.iot.mapper.ProductMapper;
import com.bihua.iot.service.IManufacturerService;
import com.bihua.iot.service.IProductPropertiesService;
import com.bihua.iot.service.IProductService;
import com.bihua.iot.service.IProductServicesService;
import com.bihua.iot.service.TdEngineService;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 产品模型Service业务层处理
 *
 * @author bihua
 * @date 2023-06-15
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class ProductServiceImpl implements IProductService {

    private final ProductMapper baseMapper;
    private final IProductServicesService iProductServicesService;
    private final IProductPropertiesService iProductPropertiesService;
    private final TdEngineService tdEngineService;
    private final IManufacturerService iManufacturerService;

    @Value("${spring.datasource.dynamic.datasource.td.dbName:bihua}")
    private String dataBaseName;
    /**
     * 查询产品模型
     */
    @Override
    public ProductVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询产品模型列表
     */
    @Override
    public TableDataInfo<ProductVo> queryPageList(ProductBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Product> lqw = buildQueryWrapper(bo);
        Page<ProductVo> result = baseMapper.selectPageList(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询产品模型列表
     */
    @Override
    public List<ProductVo> queryList(ProductBo bo) {
        LambdaQueryWrapper<Product> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Product> buildQueryWrapper(ProductBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Product> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getAppId()), Product::getAppId, bo.getAppId());
        lqw.eq(StringUtils.isNotBlank(bo.getTemplateIdentification()), Product::getTemplateIdentification, bo.getTemplateIdentification());
        lqw.like(StringUtils.isNotBlank(bo.getProductName()), Product::getProductName, bo.getProductName());
        lqw.eq(StringUtils.isNotBlank(bo.getProductIdentification()), Product::getProductIdentification, bo.getProductIdentification());
        lqw.eq(StringUtils.isNotBlank(bo.getProductType()), Product::getProductType, bo.getProductType());
        lqw.eq(StringUtils.isNotBlank(bo.getManufacturerId()), Product::getManufacturerId, bo.getManufacturerId());
        lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), Product::getManufacturerName, bo.getManufacturerName());
        lqw.eq(StringUtils.isNotBlank(bo.getModel()), Product::getModel, bo.getModel());
        lqw.eq(StringUtils.isNotBlank(bo.getDataFormat()), Product::getDataFormat, bo.getDataFormat());
        lqw.eq(StringUtils.isNotBlank(bo.getProtocolType()), Product::getProtocolType, bo.getProtocolType());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), Product::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 新增产品模型
     */
    @Override
    public Boolean insertByBo(ProductBo bo) {
        Product add = BeanUtil.toBean(bo, Product.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改产品模型
     */
    @Override
    public Boolean updateByBo(ProductBo bo) {
        Product update = BeanUtil.toBean(bo, Product.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Product entity){
        //TODO 做一些数据校验,如唯一约束
        ManufacturerVo mfrVo = iManufacturerService.queryById(Long.valueOf(entity.getManufacturerId()));
        if(ObjectUtil.isNull(mfrVo)){
            throw new ServiceException("厂商信息不正确，请核查！");
        }
        entity.setManufacturerName(mfrVo.getMfrName());
    }

    /**
     * 批量删除产品模型
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 初始化生成超级表模型
     *
     * @param productIds      产品ID集合  productIds==null 初始化所有产品:productIds!=null 初始化指定产品
     * @param InitializeOrNot 是否初始化
     * @return
     * @throws Exception
     */
    @Async
    @Override
    public List<TdSuperTable> createSuperTableDataModel(Long[] productIds, Boolean InitializeOrNot) {
        List<TdSuperTable> superTableDtoList = new ArrayList<>();
        List<ProductVo> productList;
        if (null == productIds) {
            ProductBo bo = new ProductBo();
            bo.setStatus("0");
            productList = queryList(bo);
        } else {
            productList = baseMapper.selectVoBatchIds(Arrays.asList(productIds)).stream().filter(new Predicate<ProductVo>() {
                @Override
                public boolean test(ProductVo productVo) {
                    return StringUtils.equalsIgnoreCase(productVo.getStatus(), "0");
                }
            }).collect(Collectors.toList());
        }
        if (productList.isEmpty()) {
            return superTableDtoList;
        }
        TdSuperTable superTable;
        loop:
        for (ProductVo product : productList) {
            ProductServicesBo servicesBo = new ProductServicesBo();
            servicesBo.setProductIdentification(product.getProductIdentification());
            servicesBo.setStatus("0");
            List<ProductServicesVo> allByProductIdAndStatus = iProductServicesService.queryList(servicesBo);
            if (CollectionUtil.isEmpty(allByProductIdAndStatus)) {
                continue;
            }
            for (ProductServicesVo productServices : allByProductIdAndStatus) {
                superTable = new TdSuperTable();
                if (ObjectUtil.isNull(productServices)) {
                    continue loop;
                }
                //超级表名称命名规则:产品类型_产品标识_服务名称
                String superTableName = product.getProductType() + "_" + product.getProductIdentification() + "_" + productServices.getServiceName();
                //设置数据库名称和超级表名称
                superTable.setDataBaseName(dataBaseName);
                superTable.setSuperTableName(superTableName);
                //构建超级表的表结构字段列表

                ProductPropertiesBo productPropertiesBo = new ProductPropertiesBo();
                productPropertiesBo.setServiceId(productServices.getId());
                List<ProductPropertiesVo> allByServiceId = iProductPropertiesService.queryList(productPropertiesBo);
                //如果服务下属性值为空，没必要为该服务创建超级表，跳过该循环，进入下个服务
                if (CollectionUtil.isEmpty(allByServiceId)) {
                    continue loop;
                }
                //构建超级表的表结构字段列表
                List<TdFields> schemaFields = new ArrayList<>();
                //超级表第一个字段数据类型必须为时间戳,默认Ts为当前系统时间
                TdFields tsColumn = new TdFields();
                tsColumn.setFieldName("ts");
                tsColumn.setDataType(DataTypeEnum.TIMESTAMP.getDataType());
                schemaFields.add(tsColumn);
                //超级表第二个字段为事件发生时间数据类型必须为时间戳
                TdFields eventTimeColumn = new TdFields();
                eventTimeColumn.setFieldName("event_time");
                eventTimeColumn.setDataType(DataTypeEnum.TIMESTAMP.getDataType());
                schemaFields.add(eventTimeColumn);
                //根据属性对象列表循环构建超级表表结构
                for (ProductPropertiesVo productProperties : allByServiceId) {
                    //获取字段名称
                    String filedName = productProperties.getName();
                    //获取该属性数据类型
                    String datatype = productProperties.getDatatype();
                    //获取该属性的数据大小
                    Integer size = productProperties.getMaxlength();
                    //添加超级表表结构字段
                    TdFields fields = new TdFields(filedName, datatype, size);
                    schemaFields.add(fields);
                }
                //构建超级表标签字段列表
                //根据业务逻辑，将超级表的标签字段定为
                // 1:设备标识：deviceIdentification
                List<TdFields> tagsFields = new ArrayList<>();
                TdFields tags = new TdFields();
                tags.setFieldName("device_identification");
                tags.setDataType(DataTypeEnum.BINARY.getDataType());
                tags.setSize(64);
                tagsFields.add(tags);

                //设置超级表表结构列表
                superTable.setSchemaFields(schemaFields);
                //设置超级表标签字段列表
                superTable.setTagsFields(tagsFields);
                //将之前存在redis里的同样的名称的超级表的表结构信息删除
                if (RedisUtils.hasKey(Constants.TDENGINE_SUPERTABLEFILELDS + superTableName)) {
                    RedisUtils.deleteObject(Constants.TDENGINE_SUPERTABLEFILELDS + superTableName);
                }
                //在redis里存入新的超级表对的表结构信息
                RedisUtils.setCacheObject(Constants.TDENGINE_SUPERTABLEFILELDS + superTableName, superTable);
                log.info("缓存超级表数据模型:{}", JSON.toJSONString(superTable));
                superTableDtoList.add(superTable);
                if (Boolean.TRUE.equals(InitializeOrNot)) {
                    //推送RocketMq消息初始化超级表
                    final boolean tableExists = tdEngineService.checkTableExists(dataBaseName, superTableName);
                    if (tableExists) {
                        log.info("超级表{}已存在", superTableName);
                        continue ;
                    }
                    //获取列字段对象集合的第一个对象的字段数据类型
                    String dataType = schemaFields.get(0).getDataType();
                    //如果该数据类型不是时间戳，打印和返回报错信息
                    if (dataType == null || !"timestamp".equals(dataType)) {
                        log.error("invalid operation: first column must be timestamp");
                        continue ;
                    }
                    //创建超级表
                    try {
                        tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    log.info("create {} super table success", superTableName);
             }
            }
        }
        return superTableDtoList;
    }

    @Override
    public ProductVo findOneByProductIdentification(String productIdentification) {
        LambdaQueryWrapper<Product> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotEmpty(productIdentification), Product::getProductIdentification, productIdentification);
        List<Product> voList = baseMapper.queryList(lqw);
        if(CollectionUtil.isEmpty(voList)){
            return null;
        }else {
            return BeanCopyUtils.copy(voList.get(0), ProductVo.class);
        }
    }
}
